// MediaPortal status for LCDSmartie
// Based on DemoC++Plugin.cpp : Defines the entry points for the DLL application.
// also based on work done in mce_dll.
// Dave Perrow 2012
//
// Common routines
//
#include "MP.h"
//
// Definition for linking the Kernel32 QueryFullProcessImageName function 
// for use in the check process active routine
//
typedef BOOL (WINAPI *QUERYFULLPROCESSIMAGENAME) (HANDLE hProcess, DWORD dwFlags, LPTSTR lpExeName, PDWORD lpdwSize);
QUERYFULLPROCESSIMAGENAME QueryFullProcessImageName;

//
// Character table for UTF-8 encoding routine
//
static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 
                                'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 
                                'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 
                                'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 
                                'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
                                'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 
                                'w', 'x', 'y', 'z', '0', '1', '2', '3', 
                                '4', '5', '6', '7', '8', '9', '+', '/'}; 
static char decoding_table[256]; 
static int mod_table[] = {0, 2, 1}; 



//==============================================================================
// base64 stuff for encoding a password to send via http messages 
// from http://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c
//==============================================================================

extern void __stdcall base64_encode(const char *data, size_t input_length, size_t *output_length, char *encoded_data) 
{   int i,j;
    
    *output_length = (size_t) (4.0 * ceil((double) input_length / 3.0)); 

    j=0;
    for (i = 0; i < input_length;) 
      { 
        uint32_t octet_a = i < input_length ? data[i++] : 0; 
        uint32_t octet_b = i < input_length ? data[i++] : 0; 
        uint32_t octet_c = i < input_length ? data[i++] : 0; 
        uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; 
        encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; 
        encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; 
        encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; 
        encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; 
      } 
 
    for (i = 0; i < mod_table[input_length % 3]; i++) encoded_data[*output_length - 1 - i] = '='; 
    encoded_data[*output_length] = '\0'; 
} 
 

//=========================================================================================================
// function to remove UTF8 chars from a string and replace each with a space
//
// UTF-8 Encoding:
//Bits        Last code point      Byte 1    Byte 2    Byte 3    Byte 4    Byte 5    Byte 6    byte 1 hex
//  7         U+007F               0xxxxxxx                                                         00
// 11         U+07FF               110xxxxx  10xxxxxx                                               C0
// 16         U+FFFF               1110xxxx  10xxxxxx  10xxxxxx                                     E0
// 21         U+1FFFFF             11110xxx  10xxxxxx  10xxxxxx  10xxxxxx                           F0
// 26         U+3FFFFFF            111110xx  10xxxxxx  10xxxxxx  10xxxxxx  10xxxxxx                 F8
// 31         U+7FFFFFFF           1111110x  10xxxxxx  10xxxxxx  10xxxxxx  10xxxxxx  10xxxxxx       FC
// 
//The salient features of the above scheme are as follows:
// 1.One-byte codes are used only for the ASCII values 0 through 127. In this case
//   the UTF-8 code has the same value as the ASCII code. The high-order bit of these
//   codes is always 0.
// 2.Codepoints larger than 127 are represented by multi-byte sequences, composed of
//   a leading byte and one or more continuation bytes. The leading byte has two or
//   more high-order ones, while continuation bytes all have '10' in the high-order 
//   position.
// 3.The number of high-order ones in the leading byte of a multi-byte sequence 
//   indicates the number of bytes in the sequence, so that the length of the sequence
//   can be determined without examining the continuation bytes.
// 4.Single bytes, leading bytes, and continuation bytes do not share values. This
//   makes the scheme "self synchronizing", allowing the start of a character to be
//   found by backing up at most five bytes (three bytes in actual UTF-8 as explained
//   below).
// 5.The scheme could be extended beyond 6-byte sequences and the lead bytes FE and
//   FF are available for this.
// 
//=========================================================================================================
extern void __stdcall remove_utf8(unsigned char *in)
{ int i,j,c;

  j=0;
  for (i=0;i<(strlen(in));i++)
    { c=in[i]; in[j]=c;
      if (c>0xC0) { i=i++; in[j]=' '; }
      if (c>0xE0) i++;
      if (c>0xF0) i++;
      if (c>0xF8) i++;
      if (c>0xFC) i++;
      j++;
    }
  in[j]=0;
}


//==============================================================================
// function to return bars for a 16 character width null terminated
//==============================================================================
extern void __stdcall Tbars(unsigned char *line, int val1, int val2, int bchar)
{ int j;  long int i;  unsigned char c;
  
  for (j=0; j<16; j++) line[j]='_';
  if ( (val1>0) && (val2>0) )
    {  i=val1; i=i*16; i=i/val2;
       for (j=0; j<i; j++) { c=bchar; line[j]=c; }
    }
  line[16]=0;                
}
 
//==============================================================================
// function to format a time string into 00:00:00 
//==============================================================================
extern void __stdcall time_fmt(char *str, time_t tr)
{  time_t ti;

   ti=tr;
   strcpy(str,ctime(&ti)); str[strlen(str)-1]=0;     // remove the \n put in by ctime
}

//==============================================================================
// function to format two times in secs according to the specified format 
//==============================================================================
extern void __stdcall two_time_fmt(char *line,char *fmt, int tr, int tl)
{ int tr_hours,tr_mins,tr_secs;
  int tl_hours,tl_mins,tl_secs;

  tl_hours=tl/3600; tl_mins=(tl-(tl_hours*3600))/60; tl_secs=tl%60;
  tr_hours=tr/3600; tr_mins=(tr-(tr_hours*3600))/60; tr_secs=tr%60;
  sprintf(line,fmt,tr_hours,tr_mins,tr_secs,tl_hours,tl_mins,tl_secs); 
}


//==============================================================================
// function to format a time in secs according to the specified format
//==============================================================================
extern void __stdcall MP_one_time_fmt(char *line,char *fmt, int tr)
{ int tr_hours,tr_mins,tr_secs;

  tr_hours=tr/3600; tr_mins=(tr-(tr_hours*3600))/60; tr_secs=tr%60;
  sprintf(line,fmt,tr_hours,tr_mins,tr_secs); 
}

//==============================================================================
// function to return two time strings or bars 
//==============================================================================
extern void __stdcall Ttime_fmt(char *line,char *fmt, int tr, int tl)
{ 
  if (MP_Global_usebars!=0) Tbars(line,tr,tl,MP_Global_usebars);
  else two_time_fmt(line,fmt,tr,tl);
}

//==============================================================================
// Routine to return local time as a string in format specified by user
//==============================================================================
extern char * __stdcall MP_time_string()
{  static char str[MAX_STRING];
   time_t time_since;
   struct tm * cur_time;

   time( &time_since ); cur_time =localtime( &time_since );    // get the time and convert to local time
   strftime(str, sizeof str, MP_Global_DateFormat, cur_time ); // convert to a string
   return str;
}

//==============================================================================
// function to copy a string and set the first newline character to 0
//==============================================================================
extern void __stdcall strcpytonl(char *so, char *si)
{ int i;
  for (i=0; i<strlen(si); i++) { if (si[i]=='\n') { so[i]=0; break; } else so[i]=si[i]; }
}

//==============================================================================
// function to copy a string up to char c or 0
//==============================================================================
extern int __stdcall substrcpy(unsigned char *out, unsigned char *in, unsigned char c)
{ int i,ret;

  ret=0;
  for (i=0;i<(strlen((char *)in)+1);i++)
    { out[i]=in[i]; if ((in[i]==c) || (in[i]==0)) { out[i]=0; ret=i; break; } }
  return ret;
}

//==============================================================================
// function to find a character in a string
// if found returns the index of the character past the given one
// otherwise returns 0
//==============================================================================
extern int __stdcall strfind(unsigned char *in, unsigned char c, int start)
{ int i, ret;

  ret=0;
  for (i=start;i<(strlen((char *)in));i++)
    { if (in[i]=='\\') { i=i+2; } // ignore \" etc.
      if (in[i]==c) { ret=i+1; break; } else if (in[i]==0) { ret=0; break; } 
    }
  return ret;
}

//==============================================================================
// function to find a string in a string
// if found returns the index of the character at the start of the given string
// otherwise returns -1
//==============================================================================
extern int __stdcall strfindstr(char *in, char *fstr, int len)
{ int i, ret;

  ret=(-1);
  for (i=0;i<(strlen(in));i++)
    { if (!strncmp( &in[i], fstr, len) ) { ret=i; break; } }
  return ret;
}

//==============================================================================
// Routine to get the next line from a message 
// returned null terminated in the line parameter
// returned value is the length of the line returned (0 if empty)
// ignores leading cr's and lf's 
//==============================================================================
extern int __stdcall MP_extract_line(char msgbuf[], int ptr, char line[])
{   int i,optr;
  
    optr=0; i=ptr;
    while (1)
      { if ((msgbuf[i]=='\r') || (msgbuf[i]=='\n')) i++;      // ignore crlf's
        else if (msgbuf[i]=='\0') { line[optr]='\0'; break; } // end of message found
        else
         { // We have the start of a line so copy it to the output till the EOL found
           while ((msgbuf[i]!='\n') && (msgbuf[i]!='\r') && (msgbuf[i]!='\0'))
             { line[optr++]=msgbuf[i++]; }
           line[optr]='\0'; break;
         }  
       }
    return optr;
}


//==============================================================================
// Routines to convert a mumber to an English string e.g. One,Two,Three etc.
// Handles up to 99 otherwise returns "FarTooBig"
// If negative returns "Negative"
// If 0 returns "None"
//==============================================================================

char *numbers[20] = { "None","One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten",
                      "Eleven","Twelve","Thirteen","Fourteen","Fifteen",
                      "Sixteen","Seventeen","Eighteen","Nineteen" };
char *tens[10] = { "Eek","Eeky","Twenty","Thirty","Forty","Fifty","Sixty","Seventy","Eighty","Ninety" };

char * __stdcall MP_nr_to_string(int ival)
{ int i,j;   static char str[12];

  str[0]=0;
  if (ival>99) return "FarTooBig";
  if (ival<0) return "Negative";
  if (ival<20) return numbers[ival];
  if (ival>19) strcpy(str,tens[ival/10]);
  j=ival%10; if (j>0) strcat(str,numbers[j]);
        
  return str;
}


//==============================================================================
// Routine to check if the given process is active
//==============================================================================
#if WINVER >= 0x600 
//#  define PROCESS_GET_IMAGE_NAME (PROCESS_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ) 
#  define PROCESS_GET_IMAGE_NAME (PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ) 
#else 
#  define PROCESS_GET_IMAGE_NAME (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ) 
#endif 

extern int __stdcall MP_check_process(char *procname)
{   char szProcessName[MAX_PATH] = {0};
	char str[256],buf[256],PIDbuf[1024];
	DWORD dwProcesses[1024], cbNeeded, cProcesses, dwFlags, dw;
	unsigned int i,j;  int found,slen;

    found=0;
	if (!EnumProcesses(dwProcesses, sizeof(dwProcesses), &cbNeeded)) return -1;
	cProcesses = cbNeeded / sizeof(DWORD);
	for (i = 0; i < cProcesses; i++)
	  { if(dwProcesses[i] != 0)
		{   HANDLE hProcess = OpenProcess(PROCESS_GET_IMAGE_NAME, FALSE, dwProcesses[i]);
            if (NULL != hProcess)
			{  dwFlags=0;   cbNeeded=MAX_PATH;
			   // note that the Query function used here is the only one that works in win7 x64
               if (!QueryFullProcessImageName(hProcess, dwFlags, szProcessName, &cbNeeded))
                 { dw = GetLastError(); j=(int)dw;
                   if (j!=31) 
                     { FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                          NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) buf, 256, NULL );
                       sprintf(str,"Error %d - %s\n",dw,buf); MP_errlog("check_process failed:",str);    
                     }
                  } 
               else
                 { slen=strlen(procname);
                   if (!strncmp(procname,&szProcessName[strlen(szProcessName)-slen],slen)) found=1;  
                 }
               CloseHandle(hProcess);
			}
		}
      }
    return found;
} 


//==============================================================================
// Function to Load kernel32
//==============================================================================
extern void __stdcall MP_kernel32_Start() 
{
    int i;
    char status[80];
	DWORD dw;
	char buf[256];

    HMODULE lib=LoadLibrary(TEXT("Kernel32.dll"));

//    MP_log("MP_Load_Start: ","Entered");
    if (lib==NULL) { MP_errlog("MP_Load_Start","LoadLibrary Failed"); strcpy(status,"LoadLibrary Failed"); }
    else
      { dw=0; SetLastError(dw);
        QueryFullProcessImageName=(QUERYFULLPROCESSIMAGENAME)GetProcAddress(lib, "QueryFullProcessImageNameA");
        dw = GetLastError(); 
        if (dw!=0) 
          { FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                           NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) buf, 256, NULL );
            sprintf(status,"Error %d - %s",dw,buf);
            MP_errlog("MP_Load_Start",status);
          }
       }  
//    MP_log("MP_Load_Start: ","Finished");
}
